/* Copyright (c) 2019 285424336@qq.com.
 * All rights reserved.
 * Author: 285424336
 * License: LGPLV3
 */
#include <glib.h>
#include <gio/gio.h>

#include <cstdlib>
#include <memory>

#ifdef G_OS_WIN32
#ifdef USE_VLD
#include <vld.h>
#endif
#endif

const gchar* g_address = "127.0.0.1";
constexpr guint16 g_port = 7200;
static GThread* g_thread = nullptr;

static void create_server();
static void create_thread();

/**
 * @brief main 程序入口函数
 * @param argc 启动参数个数
 * @param argv 启动参数数组
 * @return 程序返回值
 */
int main(int argc, char** argv) {
    (void)argc;
    (void)argv;
    std::shared_ptr<GMainLoop> main_loop(g_main_loop_new(nullptr, FALSE), [](GMainLoop* loop) {
        if (nullptr != loop) {
            g_main_loop_unref(loop);
        }
    });
    g_assert_nonnull(main_loop.get());
    create_server();
    create_thread();
    g_main_loop_run(main_loop.get());
    g_thread_join(g_thread);
    return EXIT_SUCCESS;
}

static gboolean connect_read_source_func(GSocket* socket,
                                         GIOCondition io_condition,
                                         gpointer user_data) {
    GSocket* connect_socket = static_cast<GSocket*>(user_data);
    do {
        gssize available_bytes = g_socket_get_available_bytes(connect_socket);
        if (-1 == available_bytes) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "-1 == available_bytes\n");
            break;
        }
        gchar* buffer = static_cast<gchar*>(g_malloc0(available_bytes));
        g_assert_nonnull(buffer);
        GError* error = nullptr;
        gssize size = g_socket_receive(connect_socket,
                                       buffer,
                                       available_bytes,
                                       nullptr,
                                       &error);
        if (nullptr != error) {
            g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
            g_error_free(error);
            error = nullptr;
        }
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "g_input_stream_read = %d\n", size);
        g_free(buffer);
    } while (0);
    return G_SOURCE_CONTINUE;
}

static gpointer thread_func(gpointer data) {
    (void)data;
    g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "thread enter\n");
    std::shared_ptr<GMainContext> thread_main_context(g_main_context_new(), [](GMainContext* main_context) {
        if (nullptr != main_context) {
            g_main_context_unref(main_context);
        }
    });
    std::shared_ptr<GMainLoop> thread_main_loop(g_main_loop_new(thread_main_context.get(), FALSE), [](GMainLoop* loop) {
        if (nullptr != loop) {
            g_main_loop_unref(loop);
        }
    });
    GError* error = nullptr;
    GSocket* connect_socket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
    if (nullptr != error) {
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
        g_error_free(error);
        error = nullptr;
        g_assert_nonnull(error);
    }
    GSocketAddress* connect_address = g_inet_socket_address_new_from_string(g_address, g_port);
    g_assert_nonnull(connect_address);
    g_socket_set_timeout(connect_socket, 5);
    gboolean boolean = g_socket_connect(connect_socket,
                                        connect_address,
                                        nullptr,
                                        &error);
    if (!boolean && (nullptr != error)) {
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "!boolean && (nullptr != error):%s\n", error->message);
        g_error_free(error);
        error = nullptr;
        g_assert_nonnull(error);
    }
    GSource* connect_source = g_socket_create_source(connect_socket,
                                                     G_IO_IN,
                                                     nullptr);
    g_assert_nonnull(connect_source);
    g_source_set_callback(connect_source, static_cast<GSourceFunc>(connect_read_source_func), connect_socket, nullptr);
    g_source_attach(connect_source, g_main_context_get_thread_default());
    g_main_loop_run(thread_main_loop.get());
    return nullptr;
}

static gboolean listen_read_source_func(GSocket* socket,
                                        GIOCondition io_condition,
                                        gpointer user_data) {
    GSocket* server_socket = static_cast<GSocket*>(user_data);
    GError* error = nullptr;
    GSocket* accept_client = g_socket_accept(server_socket, nullptr, &error);
    if (nullptr != error) {
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
        g_error_free(error);
        error = nullptr;
        g_assert_nonnull(error);
    }
    const gchar* buffer = "hello client";
    gssize size = g_socket_send(accept_client,
                                buffer,
                                sizeof(buffer),
                                nullptr,
                                &error);
    g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "size = %d\n", size);
    return G_SOURCE_CONTINUE;
}

static void create_server() {
    GError* error = nullptr;
    GSocket* listen_socket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
    if (nullptr != error) {
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
        g_error_free(error);
        error = nullptr;
        g_assert_nonnull(error);
    }
    GSocketAddress* listen_address = g_inet_socket_address_new_from_string(g_address, g_port);
    g_assert_nonnull(listen_address);
    gboolean boolean = g_socket_bind(listen_socket, listen_address, TRUE, &error);
    if (!boolean && (nullptr != error)) {
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "!boolean && (nullptr != error):%s\n", error->message);
        g_error_free(error);
        error = nullptr;
        g_assert_nonnull(error);
    }
    g_socket_set_listen_backlog(listen_socket, 1);
    boolean = g_socket_listen(listen_socket, &error);
    if (boolean && (nullptr != error)) {
        g_log(__FUNCTION__, G_LOG_LEVEL_MESSAGE, "nullptr != error:%s\n", error->message);
        g_error_free(error);
        error = nullptr;
        g_assert_nonnull(error);
    }
    GSource* accept_source = g_socket_create_source(listen_socket,
                                                    G_IO_IN,
                                                    nullptr);
    g_assert_nonnull(accept_source);
    g_source_set_callback(accept_source, static_cast<GSourceFunc>(listen_read_source_func), listen_socket, nullptr);
    g_source_attach(accept_source, g_main_context_default());
}

static void create_thread() {
    g_thread = g_thread_new("thread", thread_func, nullptr);
    g_assert_nonnull(g_thread);
}
